package com.nineclock.sign.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nineclock.common.entity.PageResult;
import com.nineclock.common.filter.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.sign.dto.SignDTO;
import com.nineclock.sign.dto.SignInfoDTO;
import com.nineclock.sign.dto.SignListDTO;
import com.nineclock.sign.dto.SignReportFormsDTO;
import com.nineclock.sign.excel.CustomHandler;
import com.nineclock.sign.mapper.SignInfoMapper;
import com.nineclock.sign.mapper.SignPictureMapper;
import com.nineclock.sign.pojo.SignInfo;
import com.nineclock.sign.pojo.SignPicture;
import com.nineclock.sign.service.SignService;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.dto.SysDepartmentDTO;
import com.nineclock.system.feign.SysCompanyUserFeign;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class SignServiceImpl implements SignService {

    @Autowired(required = false)
    private SignInfoMapper signInfoMapper;

    @Autowired(required = false)
    private SignPictureMapper signPictureMapper;

    @Autowired
    private SysCompanyUserFeign companyUserFeign;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

   /**
    * APP: 获取当前用户当天签到次数
    * */
    @Override
    public Integer getDayCount() {

        String keyStr = "sign-dayCount-"+CurrentUserHolder.get().getCompanyId()+"-"+CurrentUserHolder.get().getCompanyUserId()+"-"+DateTimeUtil.dateToStr(new Date(),DateTimeUtil.TIME_FORMAT_2);
        Integer dayCount = 0;
        String dayCountStr = redisTemplate.opsForValue().get(keyStr);
        if(!StringUtils.isEmpty(dayCountStr)){
            return dayCount;
        }

        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId());
        wrapper.eq(SignInfo::getCompanyId,CurrentUserHolder.get().getCompanyId());
        wrapper.eq(SignInfo::getCreateDate,DateTimeUtil.dateToStr(new Date(),DateTimeUtil.TIME_FORMAT_2));
        Integer count = signInfoMapper.selectCount(wrapper);

        if(StringUtils.isEmpty(dayCountStr)){
            redisTemplate.opsForValue().set(keyStr,count.toString());
        }


        return count;
    }


    /**
     *APP: 签到
     *  */

    @Override
    public void toSign(SignInfoDTO signInfoDTO) {
        SignInfo signInfo = BeanHelper.copyProperties(signInfoDTO, SignInfo.class);
        signInfo.setCompanyId(CurrentUserHolder.get().getCompanyId());
        signInfo.setCompanyUserId(CurrentUserHolder.get().getCompanyId());
        Date now = new Date();
        signInfo.setCreateTime(now);
        signInfo.setCreateDate(DateTimeUtil.dateToStr(now,DateTimeUtil.TIME_FORMAT_2));
        signInfo.setCreateMonth(DateTimeUtil.dateToStr(now,DateTimeUtil.TIME_FORMAT_4));
        signInfoMapper.insert(signInfo);

        String[] imageArrays = signInfoDTO.getImages().split(",");
        for (String imageArray : imageArrays) {
            SignPicture signPicture = new SignPicture();
            signPicture.setSignId(signInfo.getId());
            signPicture.setUrl(imageArray);
            signPictureMapper.insert(signPicture);
        }

        String keyStr = "sign-dayCount-"+signInfo.getCompanyId()+"-"+signInfo.getCompanyUserId()+"-"+DateTimeUtil.dateToStr(new Date(),DateTimeUtil.TIME_FORMAT_2);
        String dayCountStr = redisTemplate.opsForValue().get(keyStr);
        if(!StringUtils.isEmpty(dayCountStr)){
            redisTemplate.delete(keyStr);
        }

    }


    /**
     * PC: 查询签到列表
     * @param endTime
     * @param page
     * @param pageSize
     * @param startTime
     * @return
     */
    @Override
    public PageResult<SignReportFormsDTO> querySignReportForms(String endTime, Integer page, Integer pageSize, String startTime) {

        //只查员工id实现分页
        IPage<SignInfo> ipage = new Page<>(page, pageSize);
        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(SignInfo::getCompanyUserId)
                .groupBy(SignInfo::getCompanyUserId)
                .eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId())
                .between(SignInfo::getCreateDate,startTime,endTime);

        ipage = signInfoMapper.selectPage(ipage, wrapper);
        List<SignInfo> signInfoList = ipage.getRecords();

        //通过员工id的集合signInfoList给List<SignReportFormsDTO>赋值
        List<SignReportFormsDTO> signReportFormList = getSignReportFormsList(startTime, endTime, signInfoList);

        return new PageResult<>(ipage.getTotal(),ipage.getPages(),signReportFormList);
    }

    /**
     * 公共方法，把查出来的signInfoList封装成List<SignReportFormsDTO>类型的数据，并给对应的数据赋值
     * @param endTime
     * @param startTime
     * @param signInfoList
     * @return
     */
    private List<SignReportFormsDTO> getSignReportFormsList(String startTime, String endTime, List<SignInfo> signInfoList) {
        List<SignReportFormsDTO> signReportFormList = signInfoList.stream().map(signInfo -> {

            //通过员工id调用feign接口查询员工信息
            SysCompanyUserDTO companyUserDTO = companyUserFeign.queryCompanyUserById(signInfo.getCompanyUserId()).getData();
            SignReportFormsDTO signReportFormsDTO = BeanHelper.copyProperties(companyUserDTO, SignReportFormsDTO.class);

            //给部门数据赋值
            SysDepartmentDTO departmentDTO = new SysDepartmentDTO();
            departmentDTO.setId(companyUserDTO.getDepartmentId());
            departmentDTO.setName(companyUserDTO.getDepartmentName());
            signReportFormsDTO.setDepartment(departmentDTO);
            //给用户名赋值
            signReportFormsDTO.setUsername(companyUserDTO.getUserName());

            ArrayList<SignDTO> signDTOList = new ArrayList<>();
            //签到次数
            LambdaQueryWrapper<SignInfo> countWrapper = new LambdaQueryWrapper<>();
            List<String> spanDateList = DateTimeUtil.getSpanDateList(startTime, endTime);
            for (String dateStr : spanDateList) {
                //通过日期查询当天签到次数，将日期和签到次数赋值
                SignDTO signDTO = new SignDTO();

                //通过key查redis中存储的当前遍历用户当天的签到次数，如果有数据取redis
                String keyStr = "sign-dayCount-"+signInfo.getCompanyId()+"-"+signInfo.getCompanyUserId()+"-"+dateStr;
                String dayCountStr = redisTemplate.opsForValue().get(keyStr);
                if(!StringUtils.isEmpty(dayCountStr) && Integer.parseInt(dayCountStr)!=0){
                    signDTO.setSignCount(Integer.parseInt(dayCountStr));
                    signDTO.setSignDate(dateStr);
                }else{
                    //没有，走数据库，且每次查询条件清空
                    countWrapper.clear();
                    countWrapper.eq(SignInfo::getCompanyUserId, signInfo.getCompanyUserId())
                            .eq(SignInfo::getCompanyId,CurrentUserHolder.get().getCompanyId())
                            .eq(SignInfo::getCreateDate,dateStr);
                    Integer signCount = signInfoMapper.selectCount(countWrapper);

                    if(signCount!=null){
                        signDTO.setSignCount(signCount);
                        signDTO.setSignDate(dateStr);
                    }
                }

                signDTOList.add(signDTO);
            }

            if(!CollectionUtils.isEmpty(signDTOList)){
                signReportFormsDTO.setSigns(signDTOList);
            }

            return signReportFormsDTO;
        }).collect(Collectors.toList());

        return signReportFormList;
    }

    /**
     * PC: 导出签到列表
     */
    @Override
    public void exportSignData(String startTime, String endTime) throws IOException {
        // 1.获取签到列表数据
        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(SignInfo::getCompanyUserId)
                .groupBy(SignInfo::getCompanyUserId)
                .eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId())
                .between(SignInfo::getCreateDate,startTime,endTime);
        List<SignInfo> signInfoList = signInfoMapper.selectList(wrapper);

        List<SignReportFormsDTO> signReportFormsDTOList = getSignReportFormsList(startTime, endTime,signInfoList);


        // 2.获取响应对象response,设置文件下载的头信息
        //获取ServletRequestAttributes,用它可以拿到response对象
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = servletRequestAttributes.getResponse();
        response.setHeader("Content-disposition", "attachment;filename=Sign.xls");
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding("utf-8");

        List<String> spanDateList = null;
        if(!StringUtils.isEmpty(startTime)&&!StringUtils.isEmpty(endTime)){
            spanDateList = DateTimeUtil.getSpanDateList(startTime, endTime);
        }

        //3.通过response获取输出流，并生成的Excel输出
        EasyExcel.write(response.getOutputStream())
                .excelType(ExcelTypeEnum.XLS)
                .head(this.handleHeaders(spanDateList))
                .registerWriteHandler(this.horizontalCellStyleStrategy())
                .registerWriteHandler(new CustomHandler())
                .sheet("签到列表记录")
                .doWrite(this.handleData(signReportFormsDTOList,spanDateList));
    }


    /**
     * excel-处理表头
     * @return
     */
    private List<List<String>> handleHeaders(List<String> dateList) {
        List<List<String>> headers = new ArrayList<>();

        headers.add(Arrays.asList("姓名"));
        headers.add(Arrays.asList("工号"));
        headers.add(Arrays.asList("部门"));
        headers.add(Arrays.asList("职位"));


        //根据日期间隔构造excel表的头部
        if(!CollectionUtils.isEmpty(dateList)){
            for (String dateStr : dateList) {
                headers.add(Arrays.asList(dateStr));
            }
        }

        return headers;
    }


    /**
     * excel-处理每行数据
     * @param signReportFormsDTOList
     * @return
     */
    private List<List<String>> handleData(List<SignReportFormsDTO> signReportFormsDTOList,List<String> dateList) {
        List<List<String>> excelDataList = new ArrayList<>();

        if (!CollectionUtils.isEmpty(signReportFormsDTOList)) {
            for (SignReportFormsDTO signReportFormsDTO : signReportFormsDTOList) {
                // 每一行基本信息
                List<String> rowData = new ArrayList<>();
                rowData.add(signReportFormsDTO.getUsername()); // 姓名
                rowData.add(signReportFormsDTO.getWorkNumber()); // 工号
                rowData.add(signReportFormsDTO.getDepartment().getName()); // 部门
                rowData.add(signReportFormsDTO.getPost()); // 职位

                // 每一行的签到次数 根据日期计算次数
                List<SignDTO> signDTOList = signReportFormsDTO.getSigns();

                //定义一个LinkedHashMap，键是日期，值是实体类早已封装的签到次数
                LinkedHashMap<String, String> lhm = new LinkedHashMap<>();
                //通过foreach给lhm赋值
                signDTOList.stream().forEach(signDTO -> {
                    lhm.put(signDTO.getSignDate(),signDTO.getSignCount().toString());
                });

                //遍历查询的日期间隔作为键，取出lhm里的值
                dateList.stream().forEach(date->{
                    String dayCount = lhm.get(date);
                    if(!StringUtils.isEmpty(dayCount)){
                        rowData.add(dayCount);
                    }else{
                        rowData.add("0");
                    }
                });

                // 将每行数据添加到data中
                excelDataList.add(rowData);
            }
        }
        return excelDataList;
    }


    /**
     * excel-设置样式
     */
    public HorizontalCellStyleStrategy horizontalCellStyleStrategy() {

        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //内容策略 - 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        //设置字体
        WriteFont font = new WriteFont();
        font.setFontName("阿里巴巴普惠体");
        font.setFontHeightInPoints((short) 11);
        contentWriteCellStyle.setWriteFont(font);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(contentWriteCellStyle, contentWriteCellStyle);

        return horizontalCellStyleStrategy;
    }

    /**
     * APP: 查询当前登录本月签到记录
     * @param dateStr
     * @return
     */
    @Override
    public List<SignInfoDTO> querySignCurrentUserByMonth(String dateStr) {
        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyId,CurrentUserHolder.get().getCompanyId());
        wrapper.eq(SignInfo::getCompanyUserId,CurrentUserHolder.get().getCompanyUserId());

        if(!StringUtils.isEmpty(dateStr)){
            wrapper.eq(SignInfo::getCreateMonth,dateStr);
        }
        List<SignInfo> signInfoList = signInfoMapper.selectList(wrapper);
        if(!CollectionUtils.isEmpty(signInfoList)) {

            List<SignInfoDTO> signInfoDTOList = getSignInfoDTOList(dateStr, signInfoList);
            return signInfoDTOList;
        }
        return null;
    }


    /**
     * 公共方法，根据signInfoList得到signInfoDTOList
     * @param dateStr
     * @param signInfoList
     * @return
     */
    private List<SignInfoDTO> getSignInfoDTOList(String dateStr, List<SignInfo> signInfoList) {

        List<SignInfoDTO> signInfoDTOList = signInfoList.stream().map(signInfo -> {
            SignInfoDTO signInfoDTO = BeanHelper.copyProperties(signInfo, SignInfoDTO.class);
            LambdaQueryWrapper<SignInfo> tempWrapper = new LambdaQueryWrapper<>();
            tempWrapper.eq(SignInfo::getCompanyUserId, signInfo.getCompanyUserId());
            tempWrapper.eq(SignInfo::getCompanyId, signInfo.getCompanyId());
            if(dateStr.length()>7){
                //大于7就是查日期
                tempWrapper.eq(SignInfo::getCreateDate, dateStr);
            }else{
                //构造本月的时间
                String date = dateStr.substring(0, 7);
                //小于等于于7就是查日期
                tempWrapper.eq(SignInfo::getCreateMonth, date);
            }
            Integer dayCount = signInfoMapper.selectCount(tempWrapper);

            tempWrapper.clear();

            tempWrapper.eq(SignInfo::getCompanyUserId, signInfo.getCompanyUserId());
            tempWrapper.eq(SignInfo::getCompanyId, signInfo.getCompanyId());
            if(dateStr.length()>7){
                //大于7就是查日期
                tempWrapper.eq(SignInfo::getCreateDate, dateStr);
            }else{
                //构造本月的时间
                String date = dateStr.substring(0, 7);
                //小于等于于7就是查日期
                tempWrapper.eq(SignInfo::getCreateMonth, date);
            }

            Integer monthCount = signInfoMapper.selectCount(tempWrapper);

            signInfoDTO.setDayCount(dayCount.toString());
            signInfoDTO.setMonthCount(monthCount.toString());

            LambdaQueryWrapper<SignPicture> pictureWrapper = new LambdaQueryWrapper<>();
            pictureWrapper.eq(SignPicture::getSignId, signInfo.getId());
            List<SignPicture> signPictures = signPictureMapper.selectList(pictureWrapper);
            List<String> urlList = signPictures.stream().map(picture -> {
                return picture.getUrl();
            }).collect(Collectors.toList());
            signInfoDTO.setSignPictureUrlList(urlList);
            return signInfoDTO;
        }).collect(Collectors.toList());

        return signInfoDTOList;
    }

    /**
     * APP: 获取团队员工打卡列表
     * @param dateStr
     */
    @Override
    public List<SignListDTO>  listSignAllUser(String dateStr) {
        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyId,CurrentUserHolder.get().getCompanyId());
        if(!StringUtils.isEmpty(dateStr)){
            wrapper.eq(SignInfo::getCreateDate,dateStr);
        }
        List<SignInfo> signInfoList = signInfoMapper.selectList(wrapper);



        if(!CollectionUtils.isEmpty(signInfoList)){

            List<SignInfoDTO> signInfoDTOList = getSignInfoDTOList(dateStr, signInfoList);
            //把查出来的list用一个map封装，key是员工id，值是员工id对应的员工签到的list
            HashMap<Long, List<SignInfoDTO>> signMapOfUser = new HashMap<>();

            for (SignInfoDTO signInfoDTO : signInfoDTOList) {
                Long companyUserId = signInfoDTO.getCompanyUserId();
                //判断map是否有存在当前遍历的员工id的key，如果没有，初始化一个
                if(!signMapOfUser.containsKey(companyUserId)){
                    signMapOfUser.put(companyUserId,new ArrayList<>());
                }
                List<SignInfoDTO> signDTOList = signMapOfUser.get(companyUserId);
                signDTOList.add(signInfoDTO);
                signMapOfUser.put(companyUserId,signDTOList);
            }

            ArrayList<SignListDTO> signListDTOList = new ArrayList<>();
            //构造一个DTO的List，每个DTO存放每个员工签到的list和员工姓名
            for (Long companyUserId : signMapOfUser.keySet()) {
                SignListDTO signListDTO = new SignListDTO();
                signListDTO.setSignResponseList(signMapOfUser.get(companyUserId));
                SysCompanyUserDTO companyUser = companyUserFeign.queryCompanyUserById(companyUserId).getData();
                signListDTO.setUsername(companyUser.getUserName());
                signListDTOList.add(signListDTO);
            }
            if(!CollectionUtils.isEmpty(signInfoDTOList)){
                return signListDTOList;
            }
        }
        return null;
    }


}
